#pragma once

#include <optional>
#include <functional>

#include "helper/iter/Iterator.h"

namespace helper {
namespace iter {


template<typename T> 
class Skip : public Iterator<T>{
public:
  typedef typename Iterator<T>::Item Item;

  Skip(Iterator<T> & iter, size_t n) 
  : _iter(iter), _n(n) 
  {

  }
  ~Skip() override {
  }

  std::optional<Item> next() override {
    if (_n > 0){
      size_t skip = _n;
      _n = 0;
      return _iter.nth(skip);
    }
    return _iter.next();
  }

  Result<size_t> advanceBy(size_t n) override {
    size_t steps = n + _n;
    return _iter
      .advanceBy(steps)
      . template mapOrElse< helper::Result<size_t> >(
        [this, n](const auto & e) -> Result<size_t>{
          if(n != 0) {
            if(e <= _n) { //e=10, _n= 5 e<=_n
                _n -= e;
                return  Result<size_t>::Error(0);
            }
            auto ret = e - _n;
            _n = 0;
            return  Result<size_t>::Error(ret);
          }
          return Result<size_t>::OK();
        },
        [this](const auto & v) -> Result<size_t>{
          _n = 0;
          return Result<size_t>::OK();
        }
      );
  }

  
  std::optional<Item> nth(size_t n) override{
    if(_n > 0) {
      size_t skip = _n + n;
      _n = 0;
      return _iter.nth(skip);
    }
    return _iter.nth(n);
  }



  
  template<typename B>
  B fold(const B & init,  const std::function<B(const B &, const T &)> & f){
    std::cout << "Skip::Fold" << std::endl;
    if(_n > 0){
      if(!_iter.nth(_n - 1)){
        _n = 0;
        return init;
      }
      _n = 0;
    }
    return _iter.fold(init, f);
  }

  size_t count() override{
    if(_n > 0) {
      if(!_iter.nth(_n -1))
        return 0;
    }
    return _iter.count();
  }
  void foreach(const std::function<void(const T &)> & f) override{
    auto call = [](const std::function<void(const T&)> & f) -> std::function<int(const int &, const Item & )>{
        return [&f](const int & , const Item &v) -> int{
          f(v);
          return 0;
        };
    };
    fold<int>(0, call(f));
  }


  template<typename R, typename B> 
  R tryFold(const B & init, const std::function<R(const B &, const T &)> & f) {
    std::cout << "Skip::TryFold" << std::endl;
    if(_n > 0) {
      if(!_iter.nth(_n - 1)){
        _n = 0;
        return R::Continue(init);
      }
      _n = 0;
    }
    return _iter.tryFold(init, f);
  }

  //call tryFold
  virtual bool all(const std::function<bool(const T&)> & pred) {
    return tryFold<ControlFlowFlag, ControlFlowOpCode>(
      ControlFlowOpCode::BREAK,
      [&pred](const auto & , const auto & v) -> ControlFlowFlag {
        return pred(v) ? ControlFlowFlag::Continue(): ControlFlowFlag::Break();
      })
      .isContinue();
  }
  //call tryFold
  virtual bool any(const std::function<bool(const T&)> & pred) {
    return tryFold<ControlFlowFlag, ControlFlowOpCode>(
      ControlFlowOpCode::CONTINUE, 
      [&pred](const auto & , const auto & v) -> ControlFlowFlag {
        return pred(v) ? ControlFlowFlag::Break() : ControlFlowFlag::Continue();
      })
    .isBreak();
  }

  //call tryFold
  virtual std::optional<Item> find(const std::function<bool(const T&)> & pred) {
    return tryFold<ControlFlow<T, T>, T>(T(),
      [&pred](const auto &, const auto & v) -> ControlFlow<T, T> {
        return pred(v) ? ControlFlow<T, T>::Break(v) : ControlFlow<T, T>::Continue();
      }).breakValue();
  }

  //call tryFold
  template<typename B>
  std::optional<B> findMap(const std::function<std::optional<B>(const T&)> & f) {
    return tryFold<ControlFlow<B, B>, B>(
      B(),
      [&f](const auto &, const auto & v) -> ControlFlow<B, B>{
        std::optional<B> && b = f(v);
        return b ?  ControlFlow<B, B>::Break(b.value()) : ControlFlow<B, B>::Continue();
      })
    .breakValue();
  }


  //last
  //size_hint


private:
  Iterator<T> & _iter;
  size_t _n;
};

}//namespace iter
}//namespace helper
